home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Unix / satan-1.1.1 / perl / html.pl < prev    next >
Perl Script  |  1996-04-24  |  10KB  |  475 lines

  1. #!/usr/local/bin/perl
  2. #
  3. # version 1, Thu Mar 23 21:53:31 1995, last mod by wietse
  4. #
  5.  
  6. #
  7. # Run an off-the-shelf HTML client against a dedicated HTML server.  The
  8. # server executes PERL files that are specified in HTML requests.
  9. #
  10. # Authentication is magic-cookie style via the file system.  This should
  11. # be good enough: the client-server conversation never goes over the
  12. # network so the magic cookie cannot be stolen by a network sniffer.
  13. # Values in POST attribute-value lists are assigned to the corresponding
  14. # global PERL variables.  See &process_html_request() for details.
  15. #
  16.  
  17. sub html {
  18.     local($helper, $wd, $host);
  19.  
  20.     #
  21.     # Start the HTML server and generate the initial cookie for
  22.     # client-server authentication.
  23.     #
  24.     $running_from_html = 1;
  25.     chmod 0700, <~/.mosaic*>;    # Yuck!
  26.     chmod 0700, <~/.netsca*>;    # Yuck!
  27.     chmod 0700, <~/.MCOM*>;        # Yuck!
  28.     &start_html_server();
  29.     &make_password_seed();
  30.  
  31.     #
  32.     # These strings are used in, among others, PERL-to-HTML scripts.
  33.     #
  34.     $wd = `pwd`;
  35.     chop $wd;
  36.     $html_root = "$wd/html";
  37.     $start_page = "satan.html";
  38.     $THIS_HOST = &getfqdn(&hostname());
  39.     die "Can't find my own hostname: set \$dont_use_nslookup in $SATAN_CF\n"
  40.         unless $THIS_HOST;
  41.     $HTML_ROOT = "file://localhost$html_root";
  42.     $HTML_SERVER = "http://$THIS_HOST:$html_port/$html_password/$html_root";
  43.     $HTML_STARTPAGE = "$HTML_ROOT/$start_page";
  44.  
  45.     #
  46.     # Some obscurity. The real security comes from magic cookies.
  47.     #
  48.     $html_client_addresses = find_all_addresses($THIS_HOST) ||
  49.         die "Unable to find all my network addresses\n";
  50.  
  51.     for (<$html_root/*.pl>) {
  52.         s/\.pl$//;
  53.         unlink "$_.html";
  54.         open(HTML, ">$_.html")
  55.             || die "cannot write $_.html: $!\n";
  56.         select HTML;
  57.         do "$_.pl";
  58.         close HTML;
  59.         select STDOUT;
  60.         die $@ if $@;
  61.     }
  62.  
  63.     #
  64.     # Fork off the HTML client, and fork off a server process that
  65.     # handles requests from that client. The parent process waits
  66.     # until the client exits and terminates the server.
  67.     #
  68.     print "Starting $MOSAIC...\n" if $debug;
  69.  
  70.     if (($client = fork()) == 0) {
  71.         foreach (keys %ENV) {
  72.             delete $ENV{$_} if (/proxy/i && !/no_proxy/i);
  73.         }
  74.         exec($MOSAIC, "$HTML_STARTPAGE") 
  75.             || die "cannot exec $MOSAIC: $!";
  76.     } 
  77.     if (($server = fork()) == 0) {
  78.         if (($helper = fork()) == 0) {
  79.             alarm 3600;
  80.             &patience();
  81.         }
  82.         &init_satan_data();
  83.         &read_satan_data() unless defined($opt_i);
  84.         kill 'TERM',$helper;
  85.         $SIG{'PIPE'} = 'IGNORE';
  86.         for (;;) {
  87.             accept(CLIENT, SOCK) || die "accept: $!";
  88.             select((select(CLIENT), $| = 1)[0]);
  89.             &process_html_request();
  90.             close(CLIENT);
  91.         }
  92.     }
  93.  
  94.     #
  95.     # Wait until the client terminates, then terminate the server.
  96.     #
  97.     close(SOCK);
  98.     waitpid($client, 0);
  99.     kill('TERM', $server);
  100.     exit;
  101. }
  102.  
  103. #
  104. # Compute a hard to predict number for client-server authentication. Exploit
  105. # UNIX parallelism to improve unpredictability. We use MD5 only to compress
  106. # the result.
  107. #
  108. sub make_password_seed {
  109.     local($command);
  110.  
  111.     die "Cannot find $MD5. Did you run a \"reconfig\" and \"make\"?\n"
  112.         unless -x "$MD5";
  113.     $command = "ps axl&ps -el&netstat -na&netstat -s&ls -lLRt /dev*&w";
  114.     open(SEED, "($command) 2>/dev/null | $MD5 |")
  115.         || die "cannot run password command: $!";
  116.     ($html_password = <SEED>) || die "password computation failed: $!";
  117.     close(SEED);
  118.     chop($html_password);
  119. }
  120.  
  121. #
  122. # Set up a listener on an arbitrary port. There is no good reason to
  123. # listen on a well-known port number.
  124. #
  125. sub start_html_server {
  126.     local($sockaddr, $proto, $junk);
  127.  
  128.     $sockaddr = 'S n a4 x8';
  129.     ($junk, $junk, $proto) = getprotobyname('tcp');
  130.     socket(SOCK, &AF_INET, &SOCK_STREAM, $proto) || die "socket: $!";
  131.     listen(SOCK, 1) || die "listen: $!";
  132.     ($junk, $html_port) = unpack($sockaddr, getsockname(SOCK));
  133. }
  134.  
  135. #
  136. # Process one client request.  We expect the client to send stuff that
  137. # begins with:
  138. #
  139. #    command /password/perl_script junk
  140. #
  141. # Where perl_script is the name of a perl file that is executed via
  142. # do "perl_script";
  143. #
  144. # In case of a POST command the values in the client's attribute-value
  145. # list are assigned to the corresponding global PERL variables.
  146. #
  147. sub process_html_request {
  148.     local($request, $command, $script, $magic, $url, $peer);
  149.     local(%args);
  150.  
  151.     #
  152.     # Parse the command and URL. Update the default file prefix.
  153.     #
  154.     $request = <CLIENT>;
  155.     print $request if $debug;
  156.     ($command, $url) = split(/\s+/, $request);
  157.     if ($command eq "" || $command eq "QUIT") {
  158.         return;
  159.     }
  160.  
  161.     ($junk, $magic, $script) = split(/\//, $url, 3);
  162.     ($script, $html_script_args) = split(',', $script, 2);
  163.     ($HTML_CWD = "file:$script") =~ s/\/[^\/]*$//;
  164.     $HTML_CMD="/$HTML_CMD";
  165.     $script="/$script";
  166.  
  167.     #
  168.     # Make sure they gave us the right magic number.
  169.     #
  170.     if ($magic ne $html_password) {
  171.         &bad_html_magic($request);
  172.         return;
  173.     }
  174.  
  175.     #
  176.     # Assume the password has leaked out when the following happens.
  177.     #
  178.     $peer = &get_peer_addr(CLIENT);
  179.     die "SATAN password from unauthorized client: $peer\n"
  180.         unless is_member_of($peer, $html_client_addresses);
  181.     die "Illegal URL: $url received from: $peer\n" 
  182.         if index($script, "..") >= $[
  183.         || index($script, "$html_root/") != $[
  184.         || $script !~ /\.pl$/;
  185.  
  186.     #
  187.     # Warn them when the browser leaks parent URLs to web servers.
  188.     #
  189.     while (<CLIENT>) {
  190.         if (!$cookie_leak_warning && /$html_password/) {
  191.             &cookie_leak_warning();
  192.             return;
  193.         }
  194.         last if (/^\s+$/);
  195.     }
  196.  
  197.     if ($command eq "GET") {
  198.         perl_html_script($script);
  199.     } elsif ($command eq "POST") {
  200.  
  201.         #
  202.         # Process the attribute-value list.
  203.         #
  204.         if ($_ = <CLIENT>) {
  205.             s/\s+$//;
  206.             s/^/\n/;
  207.             s/&/\n/g;
  208.             $html_post_attributes = '';
  209.             $* = 1;
  210.             for (split(/(%[0-9][0-9A-Z])/, $_)) {
  211.                 $html_post_attributes .= (/%([0-9][0-9A-Z])/) ? 
  212.                     pack('c',hex($1)) : $_;
  213.             }
  214.             %args = ('_junk_', split(/\n([^=]+)=/, $html_post_attributes));
  215.             delete $args{'_junk_'};
  216.             for (keys %args) {
  217.                 print "\$$_ = $args{$_}\n" if $debug;
  218.                 ${$_} = $args{$_};
  219.             }
  220.             perl_html_script($script);
  221.         } else {
  222.             &bad_html_form($script);
  223.         }
  224.     } else {
  225.         &bad_html_command($request);
  226.     }
  227. }
  228.  
  229.  
  230. #
  231. # Map IP to string.
  232. #
  233. sub inet_ntoa {
  234.     local($ip) = @_;
  235.     local($a, $b, $c, $d);
  236.  
  237.     ($a, $b, $c, $d) = unpack('C4', $ip);
  238.     return "$a.$b.$c.$d";
  239. }
  240.  
  241. #
  242. # Look up peer address and translate to string form.
  243. #
  244. sub get_peer_addr {
  245.     local($peer) = @_;
  246.     local($junk, $inet);
  247.  
  248.     ($junk, $junk, $inet) = unpack('S n a4', getpeername($peer));
  249.     return &inet_ntoa($inet);
  250. }
  251.  
  252. #
  253. # Wrong magic number.
  254. #
  255. sub bad_html_magic {
  256.     local($request) = @_;
  257.     local($peer);
  258.  
  259.     $peer = &get_peer_addr(CLIENT);
  260.     print STDERR "bad request from $peer: $request\n";
  261.  
  262.         print CLIENT <<EOF
  263. <HTML>
  264. <HEAD>
  265. <TITLE>Bad client authentication code</TITLE>
  266. <LINK REV="made" HREF="mailto:satan\@fish.com">
  267. </HEAD>
  268. <BODY>
  269. <H1>Bad client authentication code</H1>
  270. The command: <TT>$request</TT> was not properly authenticated.
  271. </BODY>
  272. </HTML>
  273. EOF
  274. }
  275.  
  276. #
  277. # Unexpected HTML command.
  278. #
  279. sub bad_html_command {
  280.     local($request) = @_;
  281.  
  282.     print CLIENT <<EOF
  283. <HTML>
  284. <HEAD>
  285. <TITLE>Unknown command</TITLE>
  286. <LINK REV="made" HREF="mailto:satan\@fish.com">
  287. </HEAD>
  288. <BODY>
  289. <H1>Unknown command</H1>
  290. The command <TT>$request<TT> was not recognized.
  291. </BODY>
  292. </HTML>
  293. EOF
  294. }
  295.  
  296. #
  297. # Execute PERL script with extreme prejudice.
  298. #
  299. sub perl_html_script {
  300.     local($script) = @_;
  301.  
  302.     if (! -e $script) {
  303.         print CLIENT <<EOF
  304. <HTML>
  305. <HEAD>
  306. <TITLE>File not found</TITLE>
  307. <LINK REV="made" HREF="mailto:satan\@fish.com">
  308. </HEAD>
  309. <BODY>
  310. <H1>File not found</H1>
  311. The file <TT>$script</TT> does not exist or is not accessible.
  312. </BODY>
  313. </HTML>
  314. EOF
  315. ;        return;
  316.     }
  317.     do $script;
  318.     if ($@ && ($@ ne "\n")) {
  319.         print CLIENT <<EOF
  320. <HTML>
  321. <HEAD>
  322. <TITLE>Command failed</TITLE>
  323. <LINK REV="made" HREF="mailto:satan\@fish.com">
  324. </HEAD>
  325. <BODY>
  326. <H1>Command failed</H1>
  327. $@
  328. </BODY>
  329. </HTML>
  330. EOF
  331.     }
  332. }
  333.  
  334. #
  335. # Missing attribute list
  336. #
  337. sub bad_html_form {
  338.     local($script) = @_;
  339.  
  340.     print CLIENT <<EOF
  341. <HTML>
  342. <HEAD>
  343. <TITLE>No attribute list</TITLE>
  344. <LINK REV="made" HREF="mailto:satan\@fish.com">
  345. </HEAD>
  346. <BODY>
  347. <H1>No attribute list</H1>
  348.  
  349. No attribute list was found.
  350. </BODY>
  351. </HTML>
  352. EOF
  353. }
  354.  
  355. #
  356. # Scaffolding for stand-alone testing.
  357. #
  358. if ($running_under_satan == 1) {
  359.  
  360.     require 'perl/socket.pl';
  361.     require 'config/paths.pl';
  362.     require 'perl/hostname.pl';
  363.     require 'perl/getfqdn.pl';
  364.     require 'config/satan.cf';
  365.  
  366. } else {
  367.     $running_under_satan = 1;
  368.  
  369.     require 'perl/socket.pl';
  370.     require 'config/paths.pl';
  371.     require 'perl/hostname.pl';
  372.     require 'perl/getfqdn.pl';
  373.     require 'config/satan.cf';
  374.  
  375.     &html();
  376. }
  377.  
  378. #
  379. # Give them something to read while the server is initializing.
  380. #
  381. sub patience {
  382.     for (;;) {
  383.         accept(CLIENT, SOCK) || die "accept: $!";
  384.         <CLIENT>;
  385.         print CLIENT <<EOF
  386. <HTML>
  387. <HEAD>
  388. <TITLE>Initialization in progress</TITLE>
  389. <LINK REV="made" HREF="mailto:satan\@fish.com">
  390. </HEAD>
  391. <BODY>
  392. <H1>Initialization in progress</H1>
  393. SATAN is initializing, please try again later.
  394. </BODY>
  395. </HTML>
  396. EOF
  397. ;
  398.         close(CLIENT);
  399.     }
  400. }
  401.  
  402. # Look up all IP addresses listed for this host name, so that we can
  403. # filter out requests from non-local clients. Doing so offers no real
  404. # security, because network address information can be subverted.
  405. # All client-server communication security comes from the magic cookies
  406. # that are generated at program startup time. Client address filtering
  407. # adds an additional barrier in case the cookie somehow leaks out.
  408.  
  409. sub find_all_addresses {
  410.     local($host) = @_;
  411.     local($junk, $result);
  412.  
  413.     ($junk, $junk, $junk, $junk, @all_addresses) = gethostbyname($host);
  414.     for (@all_addresses) { $result .= &inet_ntoa($_) . " "; }
  415.     return $result;
  416. }
  417.  
  418. sub is_member_of {
  419.     local($elem, $list) = @_;
  420.  
  421.     for (split(/\s+/, $list)) { return 1 if ($elem eq $_); }
  422.     return 0;
  423. }
  424.  
  425. sub cookie_leak_warning {
  426.     print CLIENT <<EOF;
  427. <HTML>
  428. <HEAD>
  429. <TITLE>Warning - SATAN Password Disclosure</TITLE>
  430. <LINK REV="made" HREF="mailto:satan\@fish.com">
  431. </HEAD>
  432. <BODY>
  433. <H1><IMG SRC="$HTML_ROOT/images/satan.gif" ALT="[SATAN Image]">
  434. Warning - SATAN Password Disclosure</H1>
  435.  
  436. <HR>
  437.  
  438. <H3> 
  439.  
  440. Your Hypertext viewer may reveal confidential information when you
  441. contact remote WWW servers from within SATAN.
  442.  
  443. <p>
  444.  
  445. For this reason, SATAN advises you to not contact other WWW servers
  446. from within SATAN.
  447.  
  448. <p>
  449.  
  450. For more information, see <a
  451. href="$HTML_ROOT/tutorials/vulnerability/SATAN_password_disclosure.html">the
  452. SATAN vulnerability tutorial</a>.
  453.  
  454. <p>
  455.  
  456. This message will appear only once per SATAN session. 
  457.  
  458. <p>
  459.  
  460. In order to proceed, send a <i>reload</i> command (Ctrl-R with Lynx),
  461. or go back to the previous screen and select the same link or button
  462. again.
  463.  
  464. </H3>
  465.  
  466. </BODY>
  467. </HTML>
  468. EOF
  469.     $cookie_leak_warning = 1;
  470. }
  471.  
  472. 1;
  473.